package com.chenfan.ccp.monitor;

import com.chenfan.ccp.util.start.ApplicationContextUtil;
import io.micrometer.prometheus.PrometheusMeterRegistry;
import io.prometheus.client.Counter;
import io.prometheus.client.Gauge;
import io.prometheus.client.exporter.PushGateway;
import org.springframework.boot.actuate.metrics.export.prometheus.PrometheusPushGatewayManager;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.MalformedURLException;
import java.net.URL;


public class MonitorConfig extends OncePerRequestFilter {

    private final Gauge einprogressRequests;
    private final Counter requestCounter;
    private final PrometheusMeterRegistry prometheusMeterRegistry;
    private final PrometheusPushGatewayManager prometheusPushGatewayManager;


    public MonitorConfig(PrometheusMeterRegistry prometheusMeterRegistry, PrometheusPushGatewayManager prometheusPushGatewayManager) {
        this.prometheusMeterRegistry = prometheusMeterRegistry;
        this.einprogressRequests = einprogressRequests();
        this.requestCounter = requestCounter();
        this.prometheusPushGatewayManager = prometheusPushGatewayManager;
    }


    private PushGateway getPushGateway(String url) throws MalformedURLException {

        return new PushGateway(new URL(url));

    }

    /**
     * 刷新gateway的方法
     *
     * @param url
     * @throws NoSuchFieldException
     * @throws MalformedURLException
     * @throws IllegalAccessException
     */
    public void refresh(String url) throws NoSuchFieldException, MalformedURLException, IllegalAccessException {
        Field pushGateway = prometheusPushGatewayManager.getClass().getDeclaredField("pushGateway");
        pushGateway.setAccessible(true);
        pushGateway.set(prometheusPushGatewayManager, getPushGateway(url));

    }

    public void start(HttpServletRequest request, HttpServletResponse response) {
        String service = ApplicationContextUtil.getServiceName();
        String requestURI = request.getRequestURI();
        String method = request.getMethod();
        einprogressRequests.labels(service, requestURI, method).inc();

    }


    public void end(HttpServletRequest request, HttpServletResponse response) {
        String service = ApplicationContextUtil.getServiceName();
        String requestURI = request.getRequestURI();
        String method = request.getMethod();

        requestCounter.labels(service, requestURI, method).inc();

        einprogressRequests.labels(service, requestURI, method).dec();


    }


    /**
     * 使用Gauge可以反映应用的当前状态,例如在监控主机时，主机当前空闲的内容大小(node_memory_MemFree)，
     * 可用内存大小(node_memory_MemAvailable)。或者容器当前的CPU使用率,内存使用率。这里我们使用
     * Gauge记录当前应用正在处理的Http请求数量。
     */
    public Gauge einprogressRequests() {
        return Gauge.build()
                .name("io_namespace_http_einprogress_requests")
                .labelNames("service", "path", "method")
                .help("Einprogress requests.").register(prometheusMeterRegistry.getPrometheusRegistry());
    }


    /**
     * 计数器可以用于记录只会增加不会减少的指标类型，比如记录应用请求的总量(http_requests_total)，
     * cpu使用时间(process_cpu_seconds_total)等。 一般而言，Counter类型的metrics指标在命名中
     * 我们使用_total结束。
     */

    public Counter requestCounter() {
        return Counter.build()
                .name("io_namespace_http_requests_total")
                .labelNames("service", "path", "method")
                .help("Total requests.").register(prometheusMeterRegistry.getPrometheusRegistry());
    }


    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        try {
            start(request, response);
            filterChain.doFilter(request, response);
        } finally {
            end(request, response);
        }

    }
}